#include "Level.h"
#include "../Graphic/GraphicManager.h"
#include "../Game/Game.h"
#include "../Rules/LevelRule.h"
#include "../Rules/Statement.h"
#include <GL/glut.h>
#include "../Graphic/TextureManager.h"

bool cLevel::IsWallCharacter(char lcChar)
{
 return ((lcChar == '#') || 
   (lcChar == 'L') || (lcChar == 'R') ||(lcChar == 'U') ||(lcChar == 'D')||
   (lcChar == 'Q') || (lcChar == 'E') ||(lcChar == 'Z') ||(lcChar == 'C')||
   (lcChar == 'H') || (lcChar == 'V') ||
   (lcChar == 'T') || (lcChar == 'G') ||(lcChar == 'J') ||(lcChar == 'K')) ; 
}

//Load the map from a XML element
void cLevel::LoadMap(TiXmlElement *lpMap)
{
 unsigned int luiRowNumber = 0;

 unsigned int luiTileWidth = cGraphicManager::Get().GetTileWidth();
 unsigned int luiTileHeight = cGraphicManager::Get().GetTileHeight();

 cTexture lTexture = cTextureManager::Get().GetTexture("Data/blocks.png");
 int tex1=lTexture.GetID();
  
 miLevelList = glGenLists(1);
 glNewList(miLevelList,GL_COMPILE);
 glBindTexture(GL_TEXTURE_2D, tex1);  
 glBegin(GL_QUADS);
	
 //Process each row
 for(TiXmlElement *lpRow = lpMap->FirstChildElement(); lpRow != NULL; lpRow = lpRow->NextSiblingElement()) 
  {
    //Get the row
    std::string lsRow = lpRow->GetText();
    for (unsigned int luiIndex = 0; luiIndex < lsRow.size(); luiIndex++)
    {
      //If its a wall puts false into mWalss 
      unsigned int luiPosition = (luiRowNumber * miLevelWidth) + luiIndex;
      
      if (IsWallCharacter(lsRow[luiIndex]))
      {
        mWalls[luiPosition] = false;
        int liX = luiIndex;
        int liY = luiRowNumber;
        
        double coordx_tile = 0;
        double coordy_tile = 0;
        double cte = 0.1875f;
        if (lsRow[luiIndex] == 'L')
        {
          coordx_tile = 0.0f;
          coordy_tile = 0.25f;
        }

        if (lsRow[luiIndex] == 'R')
        {
          coordx_tile = 0.25f;
          coordy_tile = 0.25f;
        }

        if (lsRow[luiIndex] == 'U')
        {
          coordx_tile = 0.25f;
          coordy_tile = 0.0f;
        }

        if (lsRow[luiIndex] == 'D')
        {
          coordx_tile = 0.0f;
          coordy_tile = 0.0f;
        }


        if (lsRow[luiIndex] == 'Q')
        {
          coordx_tile = 0.0f;
          coordy_tile = 0.75f;
        }

        if (lsRow[luiIndex] == 'E')
        {
          coordx_tile = 0.25f;
          coordy_tile = 0.75f;
        }

        if (lsRow[luiIndex] == 'C')
        {
          coordx_tile = 0.25f;
          coordy_tile = 0.5f;
        }

        if (lsRow[luiIndex] == 'Z')
        {
          coordx_tile = 0.0f;
          coordy_tile = 0.5f;
        }


        if (lsRow[luiIndex] == 'T')
        {
          coordx_tile = 0.5f;
          coordy_tile = 0.75f;
        }

        if (lsRow[luiIndex] == 'K')
        {
          coordx_tile = 0.75f;
          coordy_tile = 0.75f;
        }

        if (lsRow[luiIndex] == 'J')
        {
          coordx_tile = 0.75f;
          coordy_tile = 0.5f;
        }

        if (lsRow[luiIndex] == 'G')
        {
          coordx_tile = 0.5f;
          coordy_tile = 0.5f;
        }

        if (lsRow[luiIndex] == '#')
        {
          coordx_tile = 0.5f;
          coordy_tile = 0.0f;
        }

        if (lsRow[luiIndex] == 'H')
        {
          coordx_tile = 0.75f;
          coordy_tile = 0;
        }

        if (lsRow[luiIndex] == 'V')
        {
          coordx_tile = 0.5f;
          coordy_tile = 0.25f;
        }


       
        glTexCoord2f(coordx_tile, coordy_tile+cte);	   
        glVertex2i(liX*luiTileWidth, liY*luiTileHeight);

			  glTexCoord2f(coordx_tile+cte, coordy_tile+cte);	
        glVertex2i((liX+1)*luiTileWidth, liY*luiTileHeight);

		    glTexCoord2f(coordx_tile+cte, coordy_tile);	
        glVertex2i((liX+1)*luiTileWidth, (liY+1)*luiTileHeight);
			  
        glTexCoord2f(coordx_tile, coordy_tile);	
        glVertex2i(liX*luiTileWidth, (liY+1)*luiTileHeight);
      }
      else
        mWalls[luiPosition] = true;

      //Add special cells to a dictionary
      if (lsRow[luiIndex] != ' ' && !IsWallCharacter(lsRow[luiIndex]))
      {
        cSpecialCell lcCell;
        lcCell.mcId = lsRow[luiIndex];
        lcCell.miX = luiIndex;
        lcCell.miY = luiRowNumber;
        mDictionary.push_back(lcCell);
      }
    }

    //Update row number
    luiRowNumber++;
  }
  glEnd();
	glEndList();
}

//Load the cells from a XML element
void cLevel::LoadCells(TiXmlElement *lpCells)
{
 
 //Process each cell
 for(TiXmlElement *lpCell = lpCells->FirstChildElement(); lpCell != NULL; lpCell = lpCell->NextSiblingElement()) 
  {
    char lcCellid = ' ';
    std::string lsType = "";
    std::string lsName = "";
    std::string lsKey = "";
    eSpecialCellContent leType = eObject;
    bool lbFound = false;
    

    //Get the attributes
    lpCell->QueryValueAttribute("cellId",&lcCellid);

    //Due a bug related with white spaces in QueryValueAttribute we have to use
    //Attribute to get strings 
    if (lpCell->Attribute("contentType"))
      lsType = lpCell->Attribute("contentType");

    if (lpCell->Attribute("name"))
      lsName = lpCell->Attribute("name");

    if (lpCell->Attribute("key"))
      lsKey = lpCell->Attribute("key");

    if (lsType == "Player")
      leType = ePlayer;

    if (lsType == "Enemy")
      leType = eEnemy;

    if (lsType == "Door")
      leType = eDoor;

    if (lsType == "Object")
      leType = eObject;

    if (lsType == "Special Location")
      leType = eSpecialLocation;

    //Seek for the element in the dictionary
    for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
    {
      //If the id id found establish the values
      if ((*lcIt).mcId == lcCellid)
      {
        (*lcIt).meCellContent = leType;
        (*lcIt).mName = lsName;


        if ((leType != eSpecialLocation))
        {
           cObject *lcObject;
           switch (leType)
           {
             //Create an instance of object
             case eObject:
               lcObject = new cObject();
               break;
             //Create an instance of door
             case eDoor:
               lcObject = new cDoor(lsKey);
               break;
             //Create an instance of player
             case ePlayer:
               lcObject = new cPlayer();
               moPlayer = lcObject;
               break;
             //Create an instance of enemy
             case eEnemy:
               lcObject = new cEnemy();               
               break;
             default:
               break;
           }

           lcObject->SetType(leType);
           lcObject->SetName(lsName);
           lcObject->SetX((*lcIt).miX);
           lcObject->SetY((*lcIt).miY);
           mObjects.push_back(lcObject);
        }

        lbFound = true;
        break;
      }
    }


    //All the special cells must exists in the map
    assert(lbFound==true);
  }
}

//Load the rules from a XML element
void cLevel::LoadRules(TiXmlElement *lpRules)
{
 

 //Process each rule
 for(TiXmlElement *lpRule = lpRules->FirstChildElement(); lpRule != NULL; lpRule = lpRule->NextSiblingElement()) 
  {
    cLevelRule lcRule;
 
    //Get every condition
    for (TiXmlElement *lpCondition = lpRule->FirstChildElement("Conds")->FirstChildElement(); lpCondition != NULL; lpCondition = lpCondition->NextSiblingElement())
    { 
      cStatement lcCondition;
      
      //Content statement
      if (lpCondition->ValueStr() == "Content")
      {
        //Set the type
        lcCondition.SetType(eStatementContent);

        //Set parameters
        if (lpCondition->Attribute("cellId"))
          lcCondition.SetParam1(lpCondition->Attribute("cellId"));
      
        if (lpCondition->Attribute("name"))
          lcCondition.SetParam2(lpCondition->Attribute("name"));
      }

      //Capture statement
      if (lpCondition->ValueStr() == "Capture")
      {
        //Set the type
        lcCondition.SetType(eStatementCapture);

        //Set the parameter
        if (lpCondition->Attribute("name"))
          lcCondition.SetParam1(lpCondition->Attribute("name"));
      }

      //Capture statement
      if (lpCondition->ValueStr() == "CaptureKind")
      {
        //Set the type
        lcCondition.SetType(eStatementCaptureKind);

        //Set the parameter
        if (lpCondition->Attribute("type"))
          lcCondition.SetParam1(lpCondition->Attribute("type"));
      }

      //Add condition
      lcRule.AddConditionStatement(lcCondition);
    }

    //Get every action
    for (TiXmlElement *lpAction = lpRule->FirstChildElement("Then")->FirstChildElement(); lpAction != NULL; lpAction = lpAction->NextSiblingElement())
    {      
      cStatement lcAction;
      
      //Remove rules
      if (lpAction->ValueStr()=="Remove")
      {
        lcAction.SetType(eStatementRemove);

        if (lpAction->Attribute("name"))
          lcAction.SetParam1(lpAction->Attribute("name"));
      }

      //Move rules
      if (lpAction->ValueStr()=="Move")
      {
        lcAction.SetType(eStatementMove);

        if (lpAction->Attribute("name"))
          lcAction.SetParam1(lpAction->Attribute("name"));
        
        if (lpAction->Attribute("cellId"))
          lcAction.SetParam2(lpAction->Attribute("cellId"));
      }

      //Get rules
      if (lpAction->ValueStr()=="Get")
      {
        lcAction.SetType(eStatementGet);
        if (lpAction->Attribute("object"))
          lcAction.SetParam1(lpAction->Attribute("object"));
      }

      //Die rules
      if (lpAction->ValueStr()=="Die")
      {
        lcAction.SetType(eStatementDie);
      }

      //Exit rules
      if (lpAction->ValueStr()=="Exit")
      {
        lcAction.SetType(eStatementExit);
      }

      lcRule.AddActionStatement(lcAction);
    }

    //Add every rule to the rules
    mRules.AddRule(lcRule);
  }
}

//Load the level from an XML file
void cLevel::Load( std::string lsFilename )
{
  TiXmlDocument lxDoc(lsFilename);

  //Load document
  bool lbLoadOk = lxDoc.LoadFile();

  //If fail goes out
  if (!lbLoadOk )
  {
    system("pause");
    exit(-1);
  }

  

 	TiXmlHandle lhDoc(&lxDoc);
	TiXmlElement *lpElem;
	TiXmlHandle lhRoot(0);

  //Seek for root element (level)
	lpElem=lhDoc.FirstChildElement().Element();
	if (!lpElem)
  {

    system("pause");
    exit(-2);
  }

  //Assign the width and height
  lpElem->QueryIntAttribute("width", &miLevelWidth);
  lpElem->QueryIntAttribute("height", &miLevelHeight);
  //Create the walls
  mWalls = new bool[miLevelWidth * miLevelHeight];

  //Read all the childs of level
  for(TiXmlElement *lpChild = lpElem->FirstChildElement(); lpChild != NULL; lpChild = lpChild->NextSiblingElement()) 
  {
 
    //If its a map Load it
    if (lpChild->ValueStr() == "Map")
      LoadMap(lpChild);

    //Load Special cells
    if (lpChild->ValueStr() == "SpecialCells")
      LoadCells(lpChild);

    //Load Rules
    if (lpChild->ValueStr() == "Rules")
      LoadRules(lpChild);
  }
}


//Return true if the position is a wall
bool cLevel::IsWall(unsigned int luiX, unsigned int luiY)
{
  unsigned int luiPosition = (luiY * miLevelWidth) + luiX;
  return !mWalls[luiPosition];
}


//Return true if the object ObjectName is in the cell CellId
bool cLevel::IsInCell(char lcCellId, std::string lsObjectName)
{

  unsigned int luiCellX;
  unsigned int luiCellY;
  bool lbFound = false;

  //Seek for the cellId
  for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
  {
    //If the id id found get the values
    if ((*lcIt).mcId == lcCellId)
    {
      luiCellX = (*lcIt).miX;
      luiCellY = (*lcIt).miY;
      lbFound = true;
    }
  }

  //CellId not found!
  if (lbFound == false) return false;

  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      //Return true if there are in the same cell, false otherwise
      return ((luiCellX == (*lcIt)->GetX()) && (luiCellY == (*lcIt)->GetY()));
    }
  }

  //Object not found!
  return false;
}

//Return true if the object lsObjectName is in the player cell 
bool cLevel::IsInPlayerCell(std::string lsObjectName)
{
  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      //Return true if there are in the same cell, false otherwise
      return (moPlayer->GetOrientation((*lcIt)->GetX(), (*lcIt)->GetY()) == eO_Over);
    }
  }

  //Object not found!
  return false;
}

//Return true if the object of type lsType is in the player cell 
bool cLevel::IsInPlayerCellKind(std::string lsType)
{
  int liType;
  
  if (lsType == "Object")
    liType = eObject;
  if (lsType == "Door")
    liType = eDoor;
  if (lsType == "Player")
    liType = ePlayer;
  if (lsType == "Special Location")
    liType = eSpecialLocation;
  if (lsType == "Enemy")
    liType = eEnemy;
  
  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetType() == liType )
    {
      //Return true if there are in the same cell, false otherwise
      if (moPlayer->GetOrientation((*lcIt)->GetX(), (*lcIt)->GetY()) == eO_Over)
        return true;
    }
  }

  //Object not found!
  return false;
}

//Add object to the list of objects
void cLevel::InsertObject(cObject *lcObject)
{
  Freeze();
  if (lcObject)
    mObjects.push_back(lcObject);
  Unfreeze();
}

//Remove object from the list of objects and delete it
void cLevel::RemoveObject(std::string lsObjectName)
{
  //Seek for object and compare position
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      delete (*lcIt);
      mObjects.erase(lcIt);
      return;
    }
  }
}


//Move object lsObjectName to lcCellId position
void cLevel::MoveObject(std::string lsObjectName, char lcCellId)
{
  unsigned int luiCellX;
  unsigned int luiCellY;
  bool lbFound = false;

  //Seek for the cellId
  for (cSpecialCellDictionaryIt lcIt = mDictionary.begin(); lcIt != mDictionary.end(); lcIt++)
  {
    //If the id id found get the values
    if ((*lcIt).mcId == lcCellId)
    {
      luiCellX = (*lcIt).miX;
      luiCellY = (*lcIt).miY;
      lbFound = true;
    }
  }

  //CellId not found!
  if (lbFound == false) return;

  //Seek for object and moveit
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      (*lcIt)->SetX(luiCellX);
      (*lcIt)->SetY(luiCellY);
      return;
    }
  }
}

//Quit level
void cLevel::QuitLevel()
{
  //Quit the game
  cGame::Get().Finish();
}


//Init the level
void cLevel::Init(const std::string lsFilename)
{ 
  msFilename = lsFilename;
  mbFreeze = false;
  //Load level
  Load(lsFilename);
}

//Freeze level update
void cLevel::Freeze()
{
  mbFreeze = true;
}

//Unfreeze level update
void cLevel::Unfreeze()
{
  mbFreeze = false;
}

//Update the level
void cLevel::Update(float lfTimeStep)
{
  if (mbFreeze) return;

  //Update player
  moPlayer->Update(lfTimeStep);

  //Update every object
  //for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  for (unsigned int i = 0; i<mObjects.size(); i++)
  {
//    (*lcIt)->Update(lfTimeStep);
    (mObjects[i])->Update(lfTimeStep);
  }

  //Evaluate rules
  mRules.EvalRules();
}

//Render the level
void cLevel::Render()
{
  //Get the graphic manager
  cGraphicManager &lGraphic = cGraphicManager::Get();

 	glEnable(GL_TEXTURE_2D);
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 /* cTexture lTexture = cTextureManager::Get().GetTexture("Data/blocks.png");
  int tex=lTexture.GetID();
  glBindTexture(GL_TEXTURE_2D, tex);  */
  glLoadIdentity();
  glColor3f(1.0,1.0,1.0);
	glCallList(miLevelList);
  glDisable(GL_TEXTURE_2D);
  //Render the objects
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    (*lcIt)->Render();
  }
	
}

//Deinit the level
void cLevel::Deinit()
{
  //Remove walls
  if (mWalls != NULL)
  {
    delete mWalls;
    mWalls = NULL;
  }

  //Remove player pointer
  moPlayer = NULL;

  //Remove objects
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    delete (*lcIt);
  }
  mObjects.clear();


  //Remove dictionary
  mDictionary.clear();
  //Rules are destroyed in destructor of the classes

  
}

//Get a vector with all Enemies
std::vector<cObject *> cLevel::GetEnemies()
{
  std::vector <cObject *> lvEnemies;

  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetType() == eEnemy)
    {
      lvEnemies.push_back((*lcIt));
    }
  }
  return lvEnemies;
}

//Give an object to the player
void cLevel::GetObject(std::string lsObjectName)
{
  bool lbFound = false;
  cObjectListIt lcIt2;
  for (cObjectListIt lcIt = mObjects.begin(); lcIt != mObjects.end(); lcIt++)
  {
    if ((*lcIt)->GetName() == lsObjectName)
    {
      lbFound = true;
      ((cPlayer *)moPlayer)->GiveObject((*lcIt));
      lcIt2 = lcIt;
      break;
    }
  }
  if (lbFound) mObjects.erase(lcIt2);
}

//Restart Level
void cLevel::Restart()
{
  cGraphicManager::Get().Init();
  int liPoints = ((cPlayer *)moPlayer)->GetPoints();
  unsigned muiLives = ((cPlayer *)moPlayer)->GetLives();
  Deinit();
  Init(msFilename);
  ((cPlayer *)moPlayer)->SetLives(muiLives - 1);
  if (liPoints>=25)
    ((cPlayer *)moPlayer)->SetPoints(liPoints - 25);
  else
    ((cPlayer *)moPlayer)->SetPoints(0);


}